"""
Redis连接和缓存管理
"""
from typing import Optional, Any
import json
import pickle
from redis import asyncio as aioredis
from redis.asyncio import Redis, ConnectionPool
from redis.exceptions import RedisError, ConnectionError as RedisConnectionError
from loguru import logger

from app.config import settings


class RedisManager:
    """Redis管理器"""

    def __init__(self):
        self.redis_client: Optional[Redis] = None
        self.cache_client: Optional[Redis] = None
        self.session_client: Optional[Redis] = None
        self.pool: Optional[ConnectionPool] = None
        self._is_initializing: bool = False  # 防止重复初始化

    async def _ensure_connection(self, client_type: str = "main") -> bool:
        """
        确保Redis连接可用，如果断开则自动重连
        
        Args:
            client_type: 客户端类型 ("main", "cache", "session")
            
        Returns:
            连接是否可用
        """
        if self._is_initializing:
            return False
        
        try:
            # 选择对应的客户端
            client = {
                "main": self.redis_client,
                "cache": self.cache_client,
                "session": self.session_client
            }.get(client_type)
            
            if not client:
                logger.warning(f"Redis {client_type} 客户端未初始化，尝试初始化...")
                await self.init()
                return True
            
            # 测试连接
            await client.ping()
            return True
            
        except (RedisConnectionError, RedisError) as e:
            logger.warning(f"Redis {client_type} 连接断开 ({e})，尝试重连...")
            try:
                await self.init()
                return True
            except Exception as reconnect_error:
                logger.error(f"Redis重连失败: {reconnect_error}")
                return False
    
    async def init(self):
        """初始化Redis连接"""
        if self._is_initializing:
            logger.info("Redis正在初始化中，跳过重复初始化")
            return
        
        self._is_initializing = True
        try:
            # 构建Redis URL
            redis_url = f"redis://{settings.REDIS_HOST}:{settings.REDIS_PORT}"
            if settings.REDIS_PASSWORD:
                redis_url = f"redis://:{settings.REDIS_PASSWORD}@{settings.REDIS_HOST}:{settings.REDIS_PORT}"

            # 创建连接池
            self.pool = ConnectionPool.from_url(
                redis_url,
                db=settings.REDIS_DB,
                max_connections=settings.REDIS_MAX_CONNECTIONS,
                socket_timeout=settings.REDIS_SOCKET_TIMEOUT,
                socket_connect_timeout=settings.REDIS_SOCKET_CONNECT_TIMEOUT,
                decode_responses=True,  # 自动解码为字符串
            )

            # 创建Redis客户端（主客户端）
            self.redis_client = Redis(connection_pool=self.pool)

            # 创建缓存专用客户端（DB 1）
            cache_pool = ConnectionPool.from_url(
                redis_url,
                db=settings.REDIS_CACHE_DB,
                max_connections=settings.REDIS_MAX_CONNECTIONS,
                decode_responses=True,
            )
            self.cache_client = Redis(connection_pool=cache_pool)

            # 创建会话专用客户端（DB 2）
            session_pool = ConnectionPool.from_url(
                redis_url,
                db=settings.REDIS_SESSION_DB,
                max_connections=settings.REDIS_MAX_CONNECTIONS,
                decode_responses=True,
            )
            self.session_client = Redis(connection_pool=session_pool)

            # 测试连接
            await self.redis_client.ping()
            logger.info(
                f"Redis连接初始化成功: {settings.REDIS_HOST}:{settings.REDIS_PORT}"
            )

        except RedisConnectionError as e:
            logger.error(f"Redis连接失败: {e}")
            raise
        except Exception as e:
            logger.error(f"Redis初始化失败: {e}")
            raise
        finally:
            self._is_initializing = False

    async def close(self):
        """关闭Redis连接"""
        try:
            if self.redis_client:
                await self.redis_client.close()
            if self.cache_client:
                await self.cache_client.close()
            if self.session_client:
                await self.session_client.close()
            if self.pool:
                await self.pool.disconnect()
            logger.info("Redis连接已关闭")
        except Exception as e:
            logger.error(f"关闭Redis连接失败: {e}")

    # ==================== 基础操作 ====================

    async def get(self, key: str) -> Optional[str]:
        """获取键值"""
        try:
            await self._ensure_connection("main")
            return await self.redis_client.get(key)
        except RedisError as e:
            logger.error(f"Redis GET错误 [{key}]: {e}")
            return None

    async def set(
        self,
        key: str,
        value: Any,
        ex: Optional[int] = None,
        px: Optional[int] = None,
        nx: bool = False,
        xx: bool = False,
    ) -> bool:
        """
        设置键值
        
        Args:
            key: 键
            value: 值
            ex: 过期时间（秒）
            px: 过期时间（毫秒）
            nx: 只在键不存在时设置
            xx: 只在键存在时设置
        """
        try:
            await self._ensure_connection("main")
            return await self.redis_client.set(key, value, ex=ex, px=px, nx=nx, xx=xx)
        except RedisError as e:
            logger.error(f"Redis SET错误 [{key}]: {e}")
            return False

    async def delete(self, *keys: str) -> int:
        """删除键"""
        try:
            return await self.redis_client.delete(*keys)
        except RedisError as e:
            logger.error(f"Redis DELETE错误: {e}")
            return 0

    async def exists(self, *keys: str) -> int:
        """检查键是否存在"""
        try:
            return await self.redis_client.exists(*keys)
        except RedisError as e:
            logger.error(f"Redis EXISTS错误: {e}")
            return 0

    async def expire(self, key: str, seconds: int) -> bool:
        """设置键过期时间"""
        try:
            return await self.redis_client.expire(key, seconds)
        except RedisError as e:
            logger.error(f"Redis EXPIRE错误 [{key}]: {e}")
            return False

    async def ttl(self, key: str) -> int:
        """获取键剩余过期时间"""
        try:
            return await self.redis_client.ttl(key)
        except RedisError as e:
            logger.error(f"Redis TTL错误 [{key}]: {e}")
            return -1

    # ==================== Hash操作 ====================

    async def hget(self, name: str, key: str) -> Optional[str]:
        """获取Hash字段值"""
        try:
            return await self.redis_client.hget(name, key)
        except RedisError as e:
            logger.error(f"Redis HGET错误 [{name}:{key}]: {e}")
            return None

    async def hset(self, name: str, key: str, value: Any) -> int:
        """设置Hash字段值"""
        try:
            return await self.redis_client.hset(name, key, value)
        except RedisError as e:
            logger.error(f"Redis HSET错误 [{name}:{key}]: {e}")
            return 0

    async def hgetall(self, name: str) -> dict:
        """获取Hash所有字段"""
        try:
            return await self.redis_client.hgetall(name)
        except RedisError as e:
            logger.error(f"Redis HGETALL错误 [{name}]: {e}")
            return {}

    async def hdel(self, name: str, *keys: str) -> int:
        """删除Hash字段"""
        try:
            return await self.redis_client.hdel(name, *keys)
        except RedisError as e:
            logger.error(f"Redis HDEL错误 [{name}]: {e}")
            return 0

    # ==================== List操作 ====================

    async def lpush(self, name: str, *values: Any) -> int:
        """从左侧插入列表"""
        try:
            return await self.redis_client.lpush(name, *values)
        except RedisError as e:
            logger.error(f"Redis LPUSH错误 [{name}]: {e}")
            return 0

    async def rpush(self, name: str, *values: Any) -> int:
        """从右侧插入列表"""
        try:
            return await self.redis_client.rpush(name, *values)
        except RedisError as e:
            logger.error(f"Redis RPUSH错误 [{name}]: {e}")
            return 0

    async def lrange(self, name: str, start: int, end: int) -> list:
        """获取列表范围"""
        try:
            return await self.redis_client.lrange(name, start, end)
        except RedisError as e:
            logger.error(f"Redis LRANGE错误 [{name}]: {e}")
            return []

    async def llen(self, name: str) -> int:
        """获取列表长度"""
        try:
            return await self.redis_client.llen(name)
        except RedisError as e:
            logger.error(f"Redis LLEN错误 [{name}]: {e}")
            return 0

    async def ltrim(self, name: str, start: int, end: int) -> bool:
        """修剪列表"""
        try:
            return await self.redis_client.ltrim(name, start, end)
        except RedisError as e:
            logger.error(f"Redis LTRIM错误 [{name}]: {e}")
            return False

    # ==================== Set操作 ====================

    async def sadd(self, name: str, *values: Any) -> int:
        """添加集合成员"""
        try:
            return await self.redis_client.sadd(name, *values)
        except RedisError as e:
            logger.error(f"Redis SADD错误 [{name}]: {e}")
            return 0

    async def smembers(self, name: str) -> set:
        """获取集合所有成员"""
        try:
            return await self.redis_client.smembers(name)
        except RedisError as e:
            logger.error(f"Redis SMEMBERS错误 [{name}]: {e}")
            return set()

    async def sismember(self, name: str, value: Any) -> bool:
        """检查是否是集合成员"""
        try:
            return await self.redis_client.sismember(name, value)
        except RedisError as e:
            logger.error(f"Redis SISMEMBER错误 [{name}]: {e}")
            return False

    async def srem(self, name: str, *values: Any) -> int:
        """移除集合成员"""
        try:
            return await self.redis_client.srem(name, *values)
        except RedisError as e:
            logger.error(f"Redis SREM错误 [{name}]: {e}")
            return 0

    # ==================== 缓存操作 ====================

    async def cache_get(self, key: str) -> Optional[Any]:
        """从缓存获取数据（自动反序列化）"""
        try:
            await self._ensure_connection("cache")
            value = await self.cache_client.get(key)
            if value:
                return json.loads(value)
            return None
        except (RedisError, json.JSONDecodeError) as e:
            logger.error(f"缓存GET错误 [{key}]: {e}")
            return None

    async def cache_set(self, key: str, value: Any, expire: int = 3600) -> bool:
        """设置缓存数据（自动序列化）"""
        try:
            await self._ensure_connection("cache")
            serialized = json.dumps(value, ensure_ascii=False)
            return await self.cache_client.set(key, serialized, ex=expire)
        except (RedisError, TypeError) as e:
            logger.error(f"缓存SET错误 [{key}]: {e}")
            return False

    async def cache_delete(self, *keys: str) -> int:
        """删除缓存"""
        try:
            return await self.cache_client.delete(*keys)
        except RedisError as e:
            logger.error(f"缓存DELETE错误: {e}")
            return 0

    async def cache_clear_pattern(self, pattern: str) -> int:
        """清除匹配模式的缓存"""
        try:
            keys = []
            async for key in self.cache_client.scan_iter(match=pattern):
                keys.append(key)
            if keys:
                return await self.cache_client.delete(*keys)
            return 0
        except RedisError as e:
            logger.error(f"清除缓存模式错误 [{pattern}]: {e}")
            return 0

    # ==================== 会话操作 ====================

    async def session_get(self, session_id: str) -> Optional[dict]:
        """获取会话数据"""
        try:
            data = await self.session_client.get(f"session:{session_id}")
            if data:
                return json.loads(data)
            return None
        except (RedisError, json.JSONDecodeError) as e:
            logger.error(f"获取会话错误 [{session_id}]: {e}")
            return None

    async def session_set(
        self, session_id: str, data: dict, expire: int = 1800
    ) -> bool:
        """设置会话数据"""
        try:
            serialized = json.dumps(data, ensure_ascii=False)
            return await self.session_client.set(
                f"session:{session_id}", serialized, ex=expire
            )
        except (RedisError, TypeError) as e:
            logger.error(f"设置会话错误 [{session_id}]: {e}")
            return False

    async def session_delete(self, session_id: str) -> bool:
        """删除会话"""
        try:
            return await self.session_client.delete(f"session:{session_id}") > 0
        except RedisError as e:
            logger.error(f"删除会话错误 [{session_id}]: {e}")
            return False

    async def session_exists(self, session_id: str) -> bool:
        """检查会话是否存在"""
        try:
            return await self.session_client.exists(f"session:{session_id}") > 0
        except RedisError as e:
            logger.error(f"检查会话错误 [{session_id}]: {e}")
            return False

    async def session_refresh(self, session_id: str, expire: int = 1800) -> bool:
        """刷新会话过期时间"""
        try:
            return await self.session_client.expire(f"session:{session_id}", expire)
        except RedisError as e:
            logger.error(f"刷新会话错误 [{session_id}]: {e}")
            return False

    # ==================== 对话历史操作 ====================

    async def save_conversation_history(
        self, user_id: str, message: dict, max_history: int = 10
    ) -> bool:
        """保存对话历史"""
        try:
            await self._ensure_connection("main")
            key = f"conversation:{user_id}"
            # 添加到列表右侧
            await self.rpush(key, json.dumps(message, ensure_ascii=False))
            # 保留最近N条
            await self.ltrim(key, -max_history, -1)
            # 设置过期时间（1小时）
            await self.expire(key, settings.CONVERSATION_MEMORY_TTL)
            return True
        except (RedisError, TypeError) as e:
            logger.error(f"保存对话历史错误 [{user_id}]: {e}")
            return False

    async def get_conversation_history(
        self, user_id: str, limit: int = 10
    ) -> list[dict]:
        """获取对话历史"""
        try:
            await self._ensure_connection("main")
            key = f"conversation:{user_id}"
            messages = await self.lrange(key, -limit, -1)
            return [json.loads(msg) for msg in messages]
        except (RedisError, json.JSONDecodeError) as e:
            logger.error(f"获取对话历史错误 [{user_id}]: {e}")
            return []

    async def clear_conversation_history(self, user_id: str) -> bool:
        """清除对话历史"""
        try:
            return await self.delete(f"conversation:{user_id}") > 0
        except RedisError as e:
            logger.error(f"清除对话历史错误 [{user_id}]: {e}")
            return False


# 全局Redis管理器实例
redis_manager = RedisManager()


# 便捷函数
async def get_redis() -> Redis:
    """获取Redis客户端"""
    return redis_manager.redis_client


async def get_cache() -> Redis:
    """获取缓存客户端"""
    return redis_manager.cache_client


# Redis健康检查
async def check_redis_health() -> bool:
    """检查Redis连接是否正常"""
    try:
        if not redis_manager.redis_client:
            return False
        await redis_manager.redis_client.ping()
        return True
    except Exception as e:
        logger.error(f"Redis健康检查失败: {e}")
        return False


# Redis初始化函数
async def init_redis():
    """初始化Redis连接"""
    await redis_manager.init()


# Redis关闭函数
async def close_redis():
    """关闭Redis连接"""
    await redis_manager.close()

